home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
t3_1
/
doc.lha
/
documentation
/
manual
/
operation.mss
< prev
next >
Wrap
Text File
|
1987-06-30
|
14KB
|
342 lines
@part[OPERATIONS, Root "TMAN.MSS"] @Comment{-*-System:TMAN-*-}
@chap[Operations]
@label[OperationsChapter] @Comment{ref: structure chapter}
@Tau[] provides a simple mechanism for programming in what
has come to be known as the @i[object-oriented style] of programming.
Object-oriented style may be contrasted to what will here be called
@i[procedural style]. In object-oriented programming, programs are
organized by giving the behavior of objects when various operations
are applied to them, whereas in procedural style, programs are organized
by giving the behavior of procedures when applied to various kinds
of objects. These styles are equivalent in terms of computational
power, but each offers different advantages in terms of readability
and modularity.
@Tau[] supports the object-oriented style with a special kind of procedure
known as an @iixs[operation], and with forms which permit one to create
objects which exhibit certain behavior when a given operation is applied
to them.
When an operation is called, the following sequence of events occurs:
@begin[itemize]
A @iixs[handler] is obtained for the object which was the operation's
first argument. (Operations must always be called with at least one argument.)
The operation asks the handler for a @iixs[method] which will handle the
operation for the object.
The handler either provides a method, or it indicates that the
object is not prepared to handle the operation.
If the handler provided a method, the method is invoked.
If not, then the operation's @iixs[default method], if any, is invoked.
If the operation has no default method, then the effect of the call to the
operation is undefined (and presumably an error condition is signalled).
@end[itemize]
In this way, an object's handler may determine how the operation
is to be performed for the object @dash[] that is, which particular method
is to be invoked as a result of invoking the operation.
Handlers map operations to methods. Many objects may have the same
handler, or a handler may be idiosyncratic to a particular object.
However, @i[every] object has a handler, so all objects participate in the
generic operation dispatch protocol.
@section[Fundamental forms]
The basis of the generic operation system consists of the two special
forms @tc[OBJECT] and @tc[OPERATION]. @tc[OBJECT]-expressions create
objects which respond appropriately to generic operations, and
@tc[OPERATION]-expressions evaluate to operations.
@index[Objects]
@info[NOTES="Special form"]
@desc[(OBJECT @i[procedure] . @i[method-clauses]) @yl[] @i[object]]
An @tc[OBJECT]-expression yields an object which is prepared to handle
generic operations according to the @i[method-clauses].
In the following description, @qu"the object" refers
to the value of a given @tc[OBJECT]-expression.
Each @i[method-clause] should be of the form
@begin[ProgramExample]
((@i[operation] . @i[variables]) . @i[body])
@end[ProgramExample]
@i[Operation] is an evaluated position, and is typically a variable
which evaluates to an operation, although it may be any
expression. When an operation is called with the object as its first
argument, the @i[operation]-expressions are evaluated,
and if one yields the operation being applied to the object, the corresponding
@i[method-clause] is selected. The operation is then performed
according to the selected @i[method-clause]: the clause's @i[variables]
are bound to the arguments to the operation,
and its @i[body], an implicit block, is evaluated.
@begin[ProgramExample]
(DEFINE OP (OPERATION NIL))
(OP (OBJECT NIL ((OP SELF) 34))) @ev[] 34
(OP (OBJECT NIL ((OP SELF X) X)) 55) @ev[] 55
@end[ProgramExample]
@i[Procedure] may be any expression, and is evaluated at the time the
@tc[OBJECT]-expression is evaluated. The object, when called, simply
calls the value of the @i[procedure] expression, passing on any
arguments. Typically @i[procedure] might be either a
@tc[LAMBDA]-expression, if the object is to be callable, or it is
@tc[NIL], which by convention means that the object is not intended to
be called, the value of @tc[NIL] being an uncallable object.
In the degenerate case, where there are no method clauses, the value of
@begin[ProgramExample]
(OBJECT (LAMBDA @i[args] . @i[body]))
@end[ProgramExample]
is indistinguishable from that of
@begin[ProgramExample]
(LAMBDA @i[args] . @i[body])@r[.]
@end[ProgramExample]
The semantics of the @tc[OBJECT] and @tc[OPERATION] special forms
can be described in terms of hypothetical primitive procedures
@tc[*OBJECT] and @tc[GET-HANDLER].
These primitives do not actually
exist in @Tau[], but are introduced here as expository aids.
@tc[*OBJECT] takes two arguments, and returns an object which, when
called, calls the object which was @tc[*OBJECT]'s first argument, and
when given to @tc[GET-HANDLER] returns the object which was
@tc[*OBJECT]'s second argument. That is,
@tc[*OBJECT] creates a two-component record (like a pair),
@tc[GET-HANDLER] extracts one component, and the other component is
called when the record is called.
@begin[ProgramExample]
@tabclear
(GET-HANDLER (*OBJECT @i[proc handler])) @^@ce[] @i[handler]
((*OBJECT @i[proc] @i[handler]) @i[arg] ...) @\@ce[] (@i[proc] @i[arg] ...)
@tabclear
@end[ProgramExample]
In addition, @tc[GET-HANDLER] is defined on @i[all] objects to return
some handler, even objects not created by @tc[*OBJECT] (if indeed
there are any such objects).
Given these primitives, the following rough equivalence holds:
@begin[ProgramExample]
(OBJECT @i[proc]
((@i[op@-[1]] . @i[args@-[1]]) . @i[body@-[1]])
((@i[op@-[2]] . @i[args@-[2]]) . @i[body@-[2]])
...
((@i[op@-[n]] . @i[args@-[n]]) . @i[body@-[n]]))
@ce[]
(*OBJECT @i[proc]
(LAMBDA (OP)
(SELECT OP
((@i[op@-[1]]) (LAMBDA @i[args@-[1]] . @i[body@-[1]]))
((@i[op@-[2]]) (LAMBDA @i[args@-[2]] . @i[body@-[2]]))
...
((@i[op@-[n]]) (LAMBDA @i[args@-[n]] . @i[body@-[n]]))
(ELSE NIL))))
@end[ProgramExample]
The outer @tc[LAMBDA]-expression yields the object's handler;
the inner @tc[LAMBDA]-expressions yield the methods, and
the mapping from operations to methods is accomplished by
the @tc[SELECT]-expression (see @tc[SELECT], page @pageref[SELECT]).
Note that the syntactic positions
@i[op@-[1]],
@i[op@-[2]],
...
@i[op@-[n]]
are evaluated positions, and the operation expressions
are evaluated when an operation is applied to the object,
not when the object is created.
@EndDesc[OBJECT]
@info[NOTES="Special form"]
@desc[(OPERATION @i[default] . @i[method-clauses]) @yl[] @i[operation]]
The syntax of @tc[OPERATION] is the same as that of @tc[OBJECT], but its
semantics and application are somewhat different. An
@tc[OPERATION]-expression evaluates to an operation. When called, the
operation obtains a handler for its first argument, calls the handler to
obtain a method, and then invokes the method. The default method for
the operation is established as being @i[default].
As the subject of another generic operation, an operation is an object
like any other, and in this case the operation acts just as if it had
been created by an @tc[OBJECT]-expression with the same @i[method-clauses].
In this way one can establish the behavior of an operation
when subject to other operations, for example @tc[SETTER].
The following rough equivalence describes the semantics of @tc[OPERATION].
Some details have been omitted.
@begin[ProgramExample]
(OPERATION @i[default] . @i[methods])
@ce[]
(LABELS ((OP (OBJECT (LAMBDA (OBJ . ARGS)
(LET ((METHOD ((GET-HANDLER OBJ) OP)))
(COND (METHOD
(APPLY METHOD OBJ ARGS))
(ELSE
(APPLY @i[default] OBJ ARGS)))))
. @i[methods])))
OP)
@end[ProgramExample]
For example:
@begin[ProgramExample]
(DEFINE OP (OPERATION (LAMBDA (OBJ) 'ZEBU)))
(OP (OBJECT NIL ((OP SELF) 'QUAGGA))) @ev[] QUAGGA
(OP 'ELAND) @ev[] ZEBU
@end[ProgramExample]
An operation is created, and the variable @tc[OP] is bound to it.
The operation's default method always returns the symbol @tc[ZEBU].
When the operation is applied to the value of the @tc[OBJECT]-expression,
the appropriate method is invoked, and the call to the operation yields
the symbol @tc[QUAGGA]. When the operation is applied to an object
which doesn't handle it @dash[] the symbol @tc[ELAND] @dash[] the operation's
default method is invoked, so the call yields the symbol @tc[ZEBU].
@EndDesc[OPERATION]
@info[NOTES="Type predicate"]
@desc[(OPERATION? @i[object]) @yl[] @i[boolean]]
Returns true if @i[object] is an operation.
@EndDesc[OPERATION?]
@section[Defining operations]
@info[NOTES="Special form"]
@desc[(DEFINE-OPERATION (@i[variable] . @i[argument-vars]) . @i[body]) @yl[] @i[undefined]]
Defines @i[variable] to be an operation.
The syntax is intended to be analogous to that of @tc[DEFINE].
The operation's default method is defined by @i[argument-vars] and @i[body].
If there is no @i[body], then the operation's default method
is undefined. In this case, the @i[argument-vars]
appear only for documentary purposes.
@begin[ProgramExample]
(DEFINE-OPERATION (@i[var] . @i[args]) . @i[body])
@ce[] (DEFINE @i[var] (OPERATION (LAMBDA @i[args] . @i[body])))
(DEFINE-OPERATION (@i[var] . @i[args]))
@ce[] (DEFINE @i[var] (OPERATION UNDEFINED-EFFECT))
@end[ProgramExample]
@EndDesc[DEFINE-OPERATION]
@info[NOTES="Special form"]
@desc[(DEFINE-SETTABLE-OPERATION (@i[variable] . @i[argument-vars]) . @i[body]) @yl[] @i[undefined]]
Defines @i[variable] to be an operation, as with @tc[DEFINE-OPERATION],
but arranges for the operation's @qu"setter" to be another operation,
so that the operation is @qu"settable" (see page @pageref[Settable]).
@begin[ProgramExample]
(DEFINE-SETTABLE-OPERATION (@i[var] . @i[args]) . @i[body])
@ce[]
(DEFINE @i[var]
(LET ((THE-SETTER (OPERATION UNDEFINED-EFFECT)))
(OPERATION (LAMBDA @i[args] . @i[body])
((SETTER SELF) THE-SETTER))))
@end[ProgramExample]
@EndDesc[DEFINE-SETTABLE-OPERATION]
@info[NOTES="Special form"]
@desc[(DEFINE-PREDICATE @i[variable]) @yl[] @i[undefined]]
Defines @i[variable] to be an operation which, by default,
returns false.
@begin[ProgramExample]
(DEFINE-PREDICATE @i[var])
@ce[]
(DEFINE-OPERATION (@i[var] OBJ) NIL)
@end[ProgramExample]
The intent is that particular @tc[OBJECT]-expressions contain clauses
of the form @wt[((@i[variable] SELF) T)]. This way the operation
defined by @tc[DEFINE-PREDICATE] may act as a type predicate that returns
true only for those objects returned by such @tc[OBJECT]-expressions.
@EndDesc[DEFINE-PREDICATE]
@dc{ Talk about how to use @tc[OBJECT] to define @qu"types." }
@section[Joined objects]
@desc[(JOIN . @i[objects]) @yl[] @i[joined-object]]
@tc[JOIN] returns an object, called a @iixs[joined object], whose
behavior is a combination of the behaviors of the @i[objects]. When an
operation is applied to the joined object, each @i[object] in turn is
given an opportunity to handle the operation; the first
to handle it does so.
@begin[ProgramExample]
(JOIN (OBJECT @i[proc@-[1]] @i[method@-[11] method@-[12] ...])
(OBJECT @i[proc@-[2]] @i[method@-[21] method@-[22] ...]))
@ce[]
(OBJECT @i[proc@-[1]] @i[method@-[11] method@-[12] ...] @i[method@-[21] method@-[22] ...])
@end[ProgramExample]
Using the hypothetical primitives described earlier, @tc[JOIN]
could be defined by the following:
@begin[ProgramExample]
(JOIN @i[first] @i[second])
@ce[]
(*OBJECT @i[first]
(LAMBDA (OP)
(OR ((GET-HANDLER @i[first]) OP)
((GET-HANDLER @i[second]) OP))))
@end[ProgramExample]
@BeginInset[Bug:]
@Timp[] 2.7 doesn't implement @tc[JOIN].
@EndInset[]
@EndDesc[JOIN]
@dc{
Talk about how to use @tc[JOIN] to implement type hierarchies and
heterarchies.
How to introduce @qu"punting"? What syntax to use? Bletch.
}
@section[Synonyms]
@iix[Synonyms] are objects whose behavior under generic operations
may vary dynamically.
@index[Call-by-name]
@info[NOTES="Special form"]
@desc[(SYNONYM @i[expression]) @yl[] @i[synonym]]
Yields a synonym for @i[expression.]
If an operation is applied to the synonym,
the @i[expression] is evaluated, yielding
another object to which the operation will then be applied.
Again using the non-existent @tc[*OBJECT] and @tc[GET-HANDLER] primitives:
@begin[ProgramExample]
(SYNONYM @i[exp])
@ce[]
(LET ((THUNK (LAMBDA () @i[exp])))
(*OBJECT (LAMBDA ARGS (APPLY (THUNK) ARGS))
(LAMBDA (OP) ((GET-HANDLER (THUNK)) OP))))
@end[ProgramExample]
@BeginInset[Bug:]
@Timp[] 2.7 doesn't implement @tc[SYNONYM].
@EndInset[]
@EndDesc[SYNONYM]
@dc{ Talk about using synonyms and @tc[JOIN] to implement objects
whose behavior may be altered through time. }
@section[Example]
Hypothetical implementation of @tc[CONS]:
@begin[ProgramExample]
(DEFINE (CONS THE-CAR THE-CDR)
(OBJECT NIL
((PAIR? SELF) T)
((CAR SELF) THE-CAR)
((CDR SELF) THE-CDR)
(((SETTER CAR) SELF NEW-CAR) (SET THE-CAR NEW-CAR))
(((SETTER CDR) SELF NEW-CDR) (SET THE-CDR NEW-CDR))))
(DEFINE-PREDICATE PAIR?)
(DEFINE-SETTABLE-OPERATION (CAR PAIR))
(DEFINE-SETTABLE-OPERATION (CDR PAIR))
@end[ProgramExample]
@dc{ Maybe insert the 6.001 queue example here. }